package com.moorror.codegenvel.utils;

import com.google.common.collect.Sets;
import com.intellij.openapi.project.Project;
import com.moorror.codegenvel.db.sqlite.entity.CodeGenTemplate;
import com.moorror.codegenvel.domain.bean.savefile.SaveFileDesc;
import com.moorror.codegenvel.domain.bean.savefile.TemplateVelocity;
import org.apache.commons.lang.StringUtils;
import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.runtime.RuntimeConstants;
import org.apache.velocity.runtime.RuntimeInstance;
import org.apache.velocity.runtime.parser.Token;
import org.apache.velocity.runtime.parser.node.*;

import java.io.PrintWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.lang.reflect.Field;
import java.util.*;

/**
 * Velocity工具类，主要用于代码生成
 *
 * @Author jiyanghuang
 * @Date 2022/2/19 10:15 PM
 */
public class VelocityUtils {
    /**
     * velocity配置
     */
    private static final Properties INIT_PROP;

    static {
        // 设置初始化配置
        INIT_PROP = new Properties();
        // 修复部分用户的velocity日志记录无权访问velocity.log文件问题
//        INIT_PROP.setProperty(RuntimeConstants.RUNTIME_LOG_INSTANCE, "org.apache.velocity.runtime.log.Log4JLogChute");
        INIT_PROP.setProperty("runtime.log.logsystem.log4j.logger", "velocity");
    }

    /**
     * 禁止创建实例对象
     */
    private VelocityUtils() {
        throw new UnsupportedOperationException();
    }

    /**
     * 渲染模板
     *
     * @param template 模板字符串
     * @param map      参数集合
     * @return 渲染结果
     */
    public static String generate(String template, Map<String, Object> map) {
        // 每次创建一个新实例，防止velocity缓存宏定义
        VelocityEngine velocityEngine = new VelocityEngine(INIT_PROP);
        // 创建上下文对象
        VelocityContext velocityContext = new VelocityContext();
        if (map != null) {
            map.forEach(velocityContext::put);
        }
        StringWriter stringWriter = new StringWriter();
        try {
            // 生成代码
            velocityEngine.evaluate(velocityContext, stringWriter, "Velocity Code Generate", template);
            Field ri = VelocityEngine.class.getDeclaredField("ri");
            ri.setAccessible(true);
            RuntimeInstance runtimeInstance = (RuntimeInstance)ri.get(velocityEngine);
            Template templateName = new Template();
            SimpleNode nodeTree = runtimeInstance.parse(new StringReader(template), templateName);
            extractVariables((ASTprocess)nodeTree);
//            System.out.println("this-->");
        } catch (Exception e) {
            // 将异常全部捕获，直接返回，用于写入模板
            StringBuilder builder = new StringBuilder("在生成代码时，模板发生了如下语法错误：\n");
            StringWriter writer = new StringWriter();
            e.printStackTrace(new PrintWriter(writer));
            builder.append(writer.toString());
            return builder.toString().replace("\r", "");
        }
        String code = stringWriter.toString();
        // 清除前面空格
        StringBuilder sb = new StringBuilder(code);
        while (sb.length() > 0 && Character.isWhitespace(sb.charAt(0))) {
            sb.deleteCharAt(0);
        }
        // 返回结果
        return sb.toString();
    }

    /**
     * 获取默认参数
     *
     * @return 参数
     */
    public static Map<String, Object> fetchDefaultParam() {
        // 系统设置
//        SettingsStorageDTO settings = SettingsStorageService.getSettingsStorage();
        Map<String, Object> param = new HashMap<>(20);
        // 作者
//        param.put("author", settings.getAuthor());
        //工具类
        param.put("toolVel", VelocityToolUtils.getInstance());
//        param.put("time", TimeUtils.getInstance());
        // 项目路径
//        param.put("projectPath", project.getBasePath());
        // Database数据库工具
//        param.put("dbUtil", ReflectionUtil.newInstance(DbUtil.class));
//        param.put("dasUtil", ReflectionUtil.newInstance(DasUtil.class));
        return param;
    }

    public static void generate(SaveFileDesc saveFileDesc, Map<String, Object> paramAll, CodeGenTemplate codeGenTemplate) {
        String code = VelocityUtils.generate(codeGenTemplate.getContent(), paramAll);
        saveFileDesc.setContent(code);
        String savePath = codeGenTemplate.getSavePath();
        String fileName = codeGenTemplate.getFileName();
        if(StringUtils.isEmpty(savePath) || StringUtils.isEmpty(fileName)){
            return;
        }else if(savePath.contains("$")){
            savePath = VelocityUtils.generate(savePath, paramAll);
        }
        if(fileName.contains("$")){
            fileName = VelocityUtils.generate(fileName, paramAll);
        }
        savePath = savePath.replace("\\", "/");
        saveFileDesc.setSavePath(savePath);
        saveFileDesc.setFileName(fileName);
//        callback.setSavePath(savePath);
        FileUtils.write(saveFileDesc);
    }

    /**
     * todo 如果没有path如何处理
     * @param paramAll
     * @param templates
     */
    public static void generate(Project project, Map<String, Object> paramAll, Collection<TemplateVelocity> templates){
        SaveFileDesc saveFileDesc = new SaveFileDesc();
        /**
         * todo 需要合并一下子模板的上下文也需要合并
         * 与父模板的上下文
         */
        for (TemplateVelocity template : templates) {
//            callback.setReformat(generateOptions.getReFormat());
            // 默认名称
//            callback.setFileName(tableInfo.getName() + "Default.java");
            // 默认路径
//            callback.setSavePath(tableInfo.getSavePath());
            // 设置回调对象
//            param.put("callback", callback);
            // 开始生成
            String code = VelocityUtils.generate(template.getCode(), paramAll);
            // 设置一个默认保存路径与默认文件名
            String path = "";
            path = path.replace("\\", "/");
            // 针对相对路径进行处理
            if (path.startsWith(".")) {
//                path = project.getBasePath() + path.substring(1);
            }
            if(template.getName().startsWith("mapper.xml")){
                path = "/Users/jiyanghuang/Documents/programLearn/b-end-management-pro/b-end-management-pro-infrastructure/src/main/resources/mapper/qms";
//                callback.setFileName(tableInfo.getNameReal() + "Mapper.xml");
            }else if(template.getName().startsWith("do")){
                path = "/Users/jiyanghuang/Documents/programLearn/b-end-management-pro/b-end-management-pro-infrastructure/src/main/java/com/moorror/bem/pro/infrastructure/qms/dos/";
//                callback.setFileName(tableInfo.getNameReal() + "DO.java");
            }else if(template.getName().startsWith("vo")){
                path = "/Users/jiyanghuang/Documents/programLearn/b-end-management-pro/b-end-management-pro-domain/src/main/java/com/moorror/bem/pro/domain/qms/";
//                callback.setFileName(tableInfo.getNameReal() + "VO.java");
            }else if(template.getName().startsWith("convert")){
                path = "/Users/jiyanghuang/Documents/programLearn/b-end-management-pro/b-end-management-pro-infrastructure/src/main/java/com/moorror/bem/pro/infrastructure/qms/convertor/";
//                callback.setFileName(tableInfo.getNameReal() + "Converter.java");

            }else if(template.getName().startsWith("mapper.java")){
                path = "/Users/jiyanghuang/Documents/programLearn/b-end-management-pro/b-end-management-pro-infrastructure/src/main/java/com/moorror/bem/pro/infrastructure/qms/mapper/";
//                callback.setFileName(tableInfo.getNameReal() + "Mapper.java");

            }else if(template.getName().startsWith("gateway")){
                path = "/Users/jiyanghuang/Documents/programLearn/b-end-management-pro/b-end-management-pro-domain/src/main/java/com/moorror/bem/pro/domain/qms/gateway/";
//                callback.setFileName(tableInfo.getNameReal() + "Gateway.java");

            }else if(template.getName().startsWith("ImplGateway")){
                path = "/Users/jiyanghuang/Documents/programLearn/b-end-management-pro/b-end-management-pro-infrastructure/src/main/java/com/moorror/bem/pro/infrastructure/qms/";
//                callback.setFileName(tableInfo.getNameReal() + "GatewayImpl.java");

            }else if(template.getName().startsWith("controller")){
                path = "/Users/jiyanghuang/Documents/programLearn/b-end-management-pro/b-end-management-pro-adaptor/src/main/java/com/moorror/bem/pro/web/controller/admin/qms";
//                callback.setFileName(tableInfo.getNameReal() + "Controller.java");

            }else if(template.getName().startsWith("pageVo")){
                path = "/Users/jiyanghuang/Documents/programLearn/b-end-management-pro/b-end-management-pro-domain/src/main/java/com/moorror/bem/pro/domain/qms/";
//                callback.setFileName(tableInfo.getNameReal() + "PageReqVO.java");
            }
            FileUtils.write(saveFileDesc);
//            new SaveFile(project, code, callback, generateOptions).write();
        }
    }

    public static Set<String> extractVariables(String template){
        // 每次创建一个新实例，防止velocity缓存宏定义
        VelocityEngine velocityEngine = new VelocityEngine(INIT_PROP);
        try {
            // 生成代码
//            velocityEngine.evaluate(velocityContext, stringWriter, "Velocity Code Generate", template);
            Field ri = VelocityEngine.class.getDeclaredField("ri");
            ri.setAccessible(true);
            RuntimeInstance runtimeInstance = (RuntimeInstance)ri.get(velocityEngine);
            Template templateName = new Template();
            SimpleNode nodeTree = runtimeInstance.parse(new StringReader(template), templateName);
            return extractVariables((ASTprocess)nodeTree);
        } catch (Exception e) {
            return Sets.newHashSet();
        }
    }
    /**
     * todo 后续得看复杂变量的声明
     * 以解决为主
     * @param asTprocess
     * @return
     */
    public static Set<String> extractVariables(ASTprocess asTprocess){
        Set<String> variables = new HashSet<>(32);
        ASTprocess rawTemplateData = asTprocess;
        if (null != rawTemplateData) {
            int nodeLength = rawTemplateData.jjtGetNumChildren();
            for (int i = 0; i < nodeLength; i++) {
                StringBuilder sb = new StringBuilder();
                // 第一级节点
                Node child = rawTemplateData.jjtGetChild(i);
                if (child instanceof ASTReference) {
                    ASTReference reference = (ASTReference) child;
                    String referenceText = variableFetch(reference);
                    sb.append(referenceText);
//                    log.info("Reference: " + referenceText);
                    int nodeGrandLen = reference.jjtGetNumChildren();
                    // 子孙节点
                    for (int j = 0; j < nodeGrandLen; j++) {
                        Node grandChild = reference.jjtGetChild(j);
                        if (grandChild instanceof ASTIdentifier) {
                            ASTIdentifier identifier = (ASTIdentifier) grandChild;
//                            sb.append("." + identifier.getIdentifier());
//                            log.info("Identifier: " + identifier.getIdentifier());
                        }
                    }
                    variables.add(sb.toString());
                }
            }
        }
        return variables;
    }

    /**
     * 参考内部写为主
     * @see SimpleNode#toString()
     * @param reference
     * @return
     */
    public static String variableFetch(ASTReference reference) {
        StringBuilder tokens = new StringBuilder();

        for (Token t = reference.getFirstToken(); t != null; t = t.next) {
            if (!(t.equals(reference.getFirstToken()) || t.equals(reference.getLastToken()))) {
                tokens.append(t.image);
            }
            if (t.next != null) {
                if (t.equals(reference.getLastToken())) {
                    break;
                }
            }
        }
        return tokens.toString();
    }
}
